#ifndef __DFU_H__
#define __DFU_H__

//定义devNum （devNum也表示组件昵称）
typedef enum 
{
    DFU_DEVNUM_WIFI_MODULE = 1, //1： WIFI模块
    DFU_DEVNUM_WIFI_LOCK,       //2： WIFI锁
    DFU_DEVNUM_FACE_MODULE,     //3： 人脸模组
    DFU_DEVNUM_VIDEO_MODULE,    //4： 视频模组
    DFU_DEVNUM_VIDEO_MCU,       //5： 视频模组MCU
    DFU_DEVNUM_FRONT_MCU,       //6： 前板
    DFU_DEVNUM_REAR_MCU,        //7： 后板
    DFU_DEVNUM_AUDIO_FLASH,     //8： 语音FLASH
    DFU_DEVNUM_SCREEN,          //9： 显示屏解码板
    DFU_DEVNUM_TOUCH_HANDLE,    //10：感应把手
    DFU_DEVNUM_DOOR_SENSOR,     //11：门磁（门窗感应器）
    DFU_DEVNUM_KEYFOB,          //12：遥控器
    DFU_DEVNUM_KEYPAD,          //13：键盘
    DFU_DEVNUM_GATEWAY,         //14：网关
    DFU_DEVNUM_UVC,             //15：UVC摄像头
    DFU_DEVNUM_FINGERVEIN,      //16：指静脉
    DFU_DEVNUM_TDKUS,           //17: 超声波模组
    DFU_DEVNUM_RADAR,           //18：雷达模组
    DFU_DEVNUM_FP,              //19：指纹模组
    DFU_DEVNUM_PVN,             //20：掌静脉模组    
    DFU_DEVNUM_LCD,             //21: LCD模块 
    DFU_DEVNUM_MAX,
    
}DFU_DevNum_enum_t;

#define KOS_DFU_DEVNUM_MAX      100    

/* 定义当前组件默认的昵称 */
#ifndef KOS_PARAM_DFU_ALIAS
#define KOS_PARAM_DFU_ALIAS DFU_DEVNUM_FRONT_MCU
#endif

/* 定义OTA结果 */
#define DFU_RESULT_OK           1       // 下载文件OK.
#define DFU_RESULT_DEVNUM_ERR   2       // devNum错误: 不在 DFU_DevNum_enum_t 之中.
#define DFU_RESULT_HEADER_ERR   3       // 文件头错误: 研发型号, 版号错误
#define DFU_RESULT_FACE_ERR     4       // 人脸模块下载错误
#define DFU_RESULT_CRC_ERR      5       // 文件烧录后, 读出校验CRC错误
#define DFU_RESULT_YMODEM_ERR   6       // ymodem传输错误
#define DFU_RESULT_WRITE_ERR    7       // 写固件数据错误（KOS主从断线了）


/* dfu nv struct */
typedef struct
{
    uint8_t model[5];    //型号
    uint8_t type[5];     //类型 MAIN/SUB
    uint8_t version[10]; //固件版本
}DfuNv_stu_t;

/* dfu publish struct */
typedef struct
{
    /* 
     * 当param==0时：表示上报需要升级的devNum
     * 当param==1时：表示上报校验APP1的结果：校验成功
     * 当param==2时：表示上报校验APP1的结果：校验失败
     * 当param==0x80时；表示上报芯片ROM-SIZE
     */
    uint8_t param;
    uint8_t devNum[4];
}DfuMessage_stu_t;


//定义DFU组件的MBOX消息类型
#define DFU_MBOX_JUMP_APP   1
#define DFU_MBOX_SET_DEVNUM 2
#define DFU_MBOX_WRITE_DATA 3
#define DFU_MBOX_START      4
#define DFU_MBOX_HEAD_INFO  5
#define DFU_MBOX_STOP       6

//跳转到APP0或APP1
#define DFU_JumpApp(devNum, appId)                       \
({                                                       \
    uint8_t temp[2] = {DFU_MBOX_JUMP_APP, appId};        \
    OSAL_MboxPost(COMP_DFU, (devNum == DFU_DEVNUM_AUDIO_FLASH) ? KOS_PARAM_DFU_ALIAS : devNum, temp, sizeof(temp)); \
})

/**
 * DFU启动接口
 * 
 * devNum：需要启动的设备
 * esn：13位esn数据
 * md5：新固件的md5值（升级结束要用这个md5来做校验）
 **/
#define DFU_Start(devNum, esn, md5)                      \
({                                                       \
    uint8_t temp[1+13+32] = {DFU_MBOX_START};            \
    memcpy(temp+1,    esn, 13);                          \
    memcpy(temp+1+13, md5, 32);                          \
    OSAL_MboxPost(COMP_DFU, devNum, temp, sizeof(temp)); \
})

#define DFU_HEAD_INFO(devNum, data, len)                 \
({                                                       \
    uint8_t temp[1+32] = {DFU_MBOX_HEAD_INFO};           \
    memcpy(temp+1, data, 32);                            \
    OSAL_MboxPost(COMP_DFU, devNum, temp, sizeof(temp)); \
})

//dfu停止接口
#define DFU_Stop(devNum)                                \
({                                                       \
    uint8_t temp[1] = {DFU_MBOX_STOP};                   \
    OSAL_MboxPost(COMP_DFU, devNum, temp, sizeof(temp)); \
})

/**
 * APP0与APP1之间数据传递接口
 * 
 * 1、APP0：BootLoader应用
 * 2、APP1：正常功能应用
 * 
 * devNum：需要升级的设备编号（APP1->APP0）
 * param1：OTA结果（APP0->APP1）
 * param2：保留
 * param3：保留
 **/
#define DFU_SetDevNum(devNum, param1, param2, param3)                        \
({                                                                           \
    uint8_t temp[5] = {DFU_MBOX_SET_DEVNUM, devNum, param1, param2, param3}; \
    OSAL_MboxPost(COMP_DFU, KOS_PARAM_DFU_ALIAS, temp, sizeof(temp));        \
})

//写固件数据（每次写64字节）
#define DFU_WriteData(devNum, opt, data, len) \
({                                            \
    uint8_t *temp = OSAL_Malloc(len + 5);     \
    ErrorStatus res = ERROR;                  \
    if (temp != NULL)                         \
    {                                         \
        temp[0] = DFU_MBOX_WRITE_DATA;        \
        temp[1] = devNum;                     \
        temp[2] = opt;                        \
        temp[3] = len;                        \
        temp[4] = len >> 8;                   \
        memcpy(&temp[5], data, len);          \
        res = OSAL_MboxPost(COMP_DFU, (devNum == DFU_DEVNUM_AUDIO_FLASH) ? KOS_PARAM_DFU_ALIAS : devNum, temp, len + 5); \
        OSAL_Free(temp); \
    }                    \
    (ErrorStatus)res;    \
})


//读取型号和TYPE用来校验OTA文件的文件头信息（仅APP0的应用层会用到）
#define DFU_GetModel(devNum, _model, _type, _len)                                              \
({                                                                                             \
    ErrorStatus res1, res2;                                                                    \
    res1 = OSAL_NvReadGlobal(COMP_DFU, devNum, OSAL_OFFSET(DfuNv_stu_t, model), _model, _len); \
    res2 = OSAL_NvReadGlobal(COMP_DFU, devNum, OSAL_OFFSET(DfuNv_stu_t, type),  _type,  _len); \
    (res1 == ERROR || res2 == ERROR) ? ERROR : SUCCESS;                                        \
})

//读取固件版本（仅APP1的应用层会用到）
#define DFU_GetVersion(devNum, ver)                                                           \
({                                                                                            \
    OSAL_NvReadGlobal(COMP_DFU, devNum, OSAL_OFFSET(DfuNv_stu_t, version), ver, sizeof(ver)); \
})

#endif /* __DFU_H__ */
